﻿namespace LambdaCalc.Core.Tests

open System
open NUnit.Framework

open LambdaCalc.Core

[<TestFixture>]
type ``test output-formating of expressings``() = 

    let format = LambdaCalc.Core.FormatedExpression.FromExpression
    let app = Application
    let fApp = FormatedApplication
    let mApp = FormatedMultiApplication
    let abst = Abstraction
    let fAbst = FormatedAbstraction
    let a = Atom
    let fA = FormatedAtom

    [<Test>]
    member test.``applies uncurrying: \x.\y.xy -> \xy.xy``() =
        let input = abst("x", abst("y", app(a "x",a "y")))
        let expectedOutput = fAbst(["x"; "y"], fApp [fA "x"; fA "y"])
        let result = format input
        Assert.AreEqual(expectedOutput, result)    

    [<Test>]
    member test.``observes left-assoziativity of application: x(yz) -> x(yz)``() =
        let input = app(a "x", app(a "y", a "z"))
        let expectedOutput = fApp [fA "x"; fApp [fA "y"; fA "z"]]
        let result = format input
        Assert.AreEqual(expectedOutput, result)    

    [<Test>]
    member test.``observes left-assoziativity of application: (xy)z -> xyz``() =
        let input = app(app(a "x", a "y"), a "z")
        let expectedOutput = fApp [fA "x"; fA "y"; fA "z"]
        let result = format input
        Assert.AreEqual(expectedOutput, result)    

    [<Test>]
    member test.``observes left-assoziativity of application: ((ab)c)d -> abcd``() =
        let input = app(app(app(a "a", a "b"), a "c"), a "d")
        let expectedOutput = fApp [fA "a"; fA "b"; fA "c"; fA "d"]
        let result = format input
        Assert.AreEqual(expectedOutput, result)    

    [<Test>]
    member test.``recognizes multiple applications: f(fa) -> f^2 a``() =
        let input = app(a "f", app(a "f", a "a"))
        let expectedOutput = mApp (fA "f", 2, [fA "a"])
        let result = format input
        Assert.AreEqual(expectedOutput, result)    

    [<Test>]
    member test.``recognizes multiple applications: f(f(fa)) -> f^3 a``() =
        let input = app (a "f", app(a "f", app(a "f", a "a")))
        let expectedOutput = mApp (fA "f", 3, [fA "a"])
        let result = format input
        Assert.AreEqual(expectedOutput, result)    

    [<Test>]
    member test.``combination: (f(f(fa))b)c -> (f^3 a)bc``() =
        let input = app (app (app (a "f", app(a "f", app(a "f", a "a"))), a "b"), a "c")
        let expectedOutput = fApp [mApp (fA "f", 3, [fA "a"]); fA "b"; fA "c"]
        let result = format input
        Assert.AreEqual(expectedOutput, result)    